/******************************************************************************/
/*                                                                            */
/*  File: main.cpp                                                            */
/*  Author: bkenwright@xbdev.net                                              */
/*  URL: www.xbdev.net                                                        */
/*                                                                            */
/******************************************************************************/
/*
	.x directx parser/drawer tutorial/demo
*/
/******************************************************************************/



#define _WIN32_WINNT 0x0400

// Enable memory leak checking, you never know, little leaks sometimes sneak
// through, and this will let us know if one has
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>


#include <windows.h>
#include <shellapi.h>
#include <stdio.h>

#pragma comment(lib, "D3d9.lib")
#pragma comment(lib, "D3dx9.lib")
#include <d3dx9.h>

#include "common\common.h"
#include "xtxtloader.h"
#include "xrender.h"


#define ID_FILE_OPEN 9001




LPDIRECT3D9			g_pD3D				= NULL;
LPDIRECT3DDEVICE9	g_pD3DDevice		= NULL;
HWND				g_hWnd				= NULL;
stXFile*			g_xFile;
float				g_timeStep			= 0.0f;
static float		g_zoom				= 1.0f;
bool				g_leftMouseButton	= false;
bool				g_keys[0xffff]		= {false};
bool				g_wire				= false;
bool				g_paused			= false;
bool				g_staticPose		= false;
bool				g_drawNormals		= false;
bool				g_drawBones			= false;
bool				g_transparent		= false;

//------------------------------------------------------------------------------

bool CreateDX()
{
	g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	D3DDISPLAYMODE d3ddm;
	g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);


	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory(&d3dpp, sizeof(d3dpp));

	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = d3ddm.Format;
	d3dpp.BackBufferCount=1;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.EnableAutoDepthStencil = TRUE;

	HRESULT hr =
		g_pD3D->CreateDevice(	D3DADAPTER_DEFAULT, 
								D3DDEVTYPE_HAL,
								g_hWnd, 
								D3DCREATE_MIXED_VERTEXPROCESSING,
								&d3dpp, 
								&g_pD3DDevice);
	if (hr!=S_OK)
	{
		hr =
		g_pD3D->CreateDevice(	D3DADAPTER_DEFAULT, 
								D3DDEVTYPE_HAL,
								g_hWnd, 
								D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
								&d3dpp, 
								&g_pD3DDevice);
	}

	// If the graphics card doesn't support hardware acceleration
	if (hr!=S_OK)
	{
		hr =
		g_pD3D->CreateDevice(	D3DADAPTER_DEFAULT, 
								D3DDEVTYPE_REF,
								g_hWnd, 
								D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
								&d3dpp, 
								&g_pD3DDevice);
	}

	g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

	g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); 
	//g_pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); 
	//g_pD3DDevice->SetRenderState(D3DRS_ALPHAREF, (DWORD)8); 
	//g_pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);



	g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

	g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE); 
	g_pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE); 

	return true;
}// End CreateDX()

bool ReleaseDX()
{
	g_pD3DDevice->Release();
	g_pD3DDevice = NULL;

	g_pD3D->Release();
	g_pD3D = NULL;
	return true;
}// End ReleaseDX()

char g_dbg[128] = {0};
static bool g_rightMouseDown = false;
static POINT g_oldPoint = {0,0};


static D3DXVECTOR3 camPosAt = D3DXVECTOR3(0,0,0);

void SetCamera()
{
	static D3DXVECTOR3 dir = D3DXVECTOR3(0,  0,  100);
	static D3DXVECTOR3 pos = D3DXVECTOR3(0,  0, -100);
	D3DXVec3Normalize(&dir, &dir);

	POINT pt;
	GetCursorPos(&pt);
	ScreenToClient(g_hWnd, &pt);
	static POINT oldPoint = pt;
	static D3DXVECTOR3 oldDir	= dir;
	static D3DXVECTOR3 oldPos	= pos;
	static float oldZoom		= g_zoom;
	static D3DXVECTOR3 oldAt    = camPosAt;


	if (g_leftMouseButton && !g_rightMouseDown)
	{
		float rotSpeed = 0.005f;
		float deltax;
		float deltay;
		deltax = (oldPoint.x - pt.x)*rotSpeed;
		deltay = (oldPoint.y - pt.y)*rotSpeed;


		D3DXMATRIX matRot;
		::D3DXMatrixRotationYawPitchRoll(&matRot, 
										 deltax,	// Yaw
										 deltay,	// Pitch
										 0);		// Roll
		D3DXVec3TransformCoord(&dir, &oldDir, &matRot);
	}
	else if(g_rightMouseDown && !g_leftMouseButton)
	{
		float zoomSpeed = 0.01f;
		float delta = (oldPoint.y - pt.y)*zoomSpeed;
		
		g_zoom = oldZoom + delta;
	}
	else if(g_rightMouseDown && g_leftMouseButton)
	{
		float moveSpeed = 0.1f;
		float deltay;
		float deltax;
		deltay = (oldPoint.y - pt.y)*moveSpeed;
		deltax = (oldPoint.x - pt.x)*moveSpeed;
		camPosAt.y   = oldAt.y  - deltay;
		pos.y		 = oldPos.y - deltay;

		camPosAt.x   = oldAt.x  + deltax;
		pos.x		 = oldPos.x + deltax;

		oldDir		= dir;
		oldZoom		= g_zoom;
	}
	else
	{
		oldPoint	= pt;
		oldDir		= dir;
		oldPos		= pos;
		oldZoom		= g_zoom;
		oldAt		= camPosAt;
	}

	D3DXVECTOR3 vEye = camPosAt - dir*g_zoom*100.0f;
	D3DXVECTOR3 vAt  = camPosAt;

    D3DXMATRIX matView;
    D3DXMatrixLookAtLH( &matView, &vEye, 
                                  &vAt, 
                                  &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
    g_pD3DDevice->SetTransform( D3DTS_VIEW, &matView );

    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, 1.0f, 0.05f, 90000.0f );
    g_pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}


void SetLight()
{
	//Set a light up?
	D3DLIGHT9 d3dLight;

	//Initialize the light structure.
	ZeroMemory(&d3dLight, sizeof(D3DLIGHT9));

	//Set up a white point light at (0, 0, -10).
	d3dLight.Type = D3DLIGHT_DIRECTIONAL; //D3DLIGHT_POINT;

	// Actual colour of the light. (e.g. white).
	d3dLight.Diffuse.r = 1.0f;
	d3dLight.Diffuse.g = 1.0f;
	d3dLight.Diffuse.b = 1.0f;

	// Colour when no light hits the object.
	d3dLight.Ambient.r = 0.5f;
	d3dLight.Ambient.g = 0.5f;
	d3dLight.Ambient.b = 0.5f;

	// Shiny effect.
	d3dLight.Specular.r = 0.0f;
	d3dLight.Specular.g = 0.0f;
	d3dLight.Specular.b = 0.0f;

	d3dLight.Direction.x = 0.0f;
	d3dLight.Direction.y = 0.2f;
	d3dLight.Direction.z = 1.0f;

	d3dLight.Position.x = 0.0f;
	d3dLight.Position.y = 0.0f;
	d3dLight.Position.z = -500.0f;

	d3dLight.Attenuation0 = 0.1f; 
	d3dLight.Attenuation1 = 0.0f; 
	d3dLight.Attenuation2 = 0.0f; 
	d3dLight.Range = 5000.0f;

	g_pD3DDevice->SetLight(0, &d3dLight);
	g_pD3DDevice->LightEnable(0,TRUE);

	//Turn on lighting
	//g_pD3DDevice->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);
	g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
	g_pD3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(100,100,100));
}



void KeyPresses()
{
	if (GetKeyState('W') & 0x80) { g_wire			= !g_wire; }
	if (GetKeyState('P') & 0x80) { g_paused			= !g_paused; }
	if (GetKeyState('S') & 0x80) { g_staticPose		= !g_staticPose; }
	if (GetKeyState('N') & 0x80) { g_drawNormals	= !g_drawNormals; }
	if (GetKeyState('B') & 0x80) { g_drawBones		= !g_drawBones; }
	if (GetKeyState('T') & 0x80) { g_transparent	= !g_transparent; }

}// End of KeyPresses(..)


void GameLoopDX()
{
	if(!g_pD3DDevice)return;


	// We use a more accurate timing information, QueryPerformanceCounter
	// instead of GetTickCount() so we get smoother animations
	LARGE_INTEGER freq;
	QueryPerformanceFrequency(&freq);

	LARGE_INTEGER nTime;
	QueryPerformanceCounter(&nTime);

	static LARGE_INTEGER oTime = nTime;

	double delay = (double(nTime.QuadPart - oTime.QuadPart)/freq.QuadPart);
	oTime=nTime;

	g_timeStep = (float)delay;

	if (g_timeStep > 1.0f)
	{
		g_timeStep = 1.0f;
	}

	static float updateFPS = 0.0f;
	updateFPS += (float)delay;
	static float fps = 1.0f/(float)delay;
	if (updateFPS>1.0f)
	{
		updateFPS=0.0f;
		fps = 1.0f/(float)delay;
	}

	extern HWND g_hWnd;
	static char title[256] = {0};

	sprintf(title, "X Txt Loader Demo - Parser/Drawer-FPS:%.1f,  TimeStep:%.2f", fps, g_timeStep);
	SetWindowText(g_hWnd, title);

	SetCamera();

	SetLight();

	KeyPresses();

	if (g_wire)	g_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
	else		g_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );


	g_pD3DDevice->BeginScene();

	g_pD3DDevice->Clear(0, 
						NULL, 
						D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 
						D3DCOLOR_XRGB(88,88,88), 1.0f, 0 );

	DebugDrawCross3D(g_pD3DDevice, camPosAt, 0.2f, 0xff00ff00);


	if (!g_paused)
		XUpdate(g_xFile);
	XRender(g_xFile);

	g_pD3DDevice->EndScene();
	g_pD3DDevice->Present( NULL, NULL, NULL, NULL );
}// End GameLoopDX()

//------------------------------------------------------------------------------



long _stdcall MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	OPENFILENAME ofn;
	char Filename[MAX_PATH] = {0};

	switch (uMsg)
	{
		case WM_KEYDOWN:
		{
			switch( wParam )
			{
				case VK_ESCAPE:         // Exit on pressing Escape Key
					PostQuitMessage(0);
					break;
				case VK_TAB:	g_keys[VK_TAB]   =true;	 break;
				case VK_RIGHT:	g_keys[VK_RIGHT] =true;	 break;
				case VK_LEFT:	g_keys[VK_LEFT]  =true;	 break;
				case VK_RETURN:	g_keys[VK_RETURN]=true;  break;
			}
		}
		break;

		case WM_MOUSEWHEEL:
		{
			float val = float(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA);

			const float zoomSpeed = 0.001f;
			g_zoom += val * zoomSpeed;
		}
		break;

		case WM_LBUTTONDOWN:
		{
			g_leftMouseButton = true;
		}
		break;

		case WM_LBUTTONUP:
		{
			g_leftMouseButton = false;
		}
		break;

		case WM_RBUTTONDOWN:
		{
			g_rightMouseDown = true;
		}
		break;

		case WM_RBUTTONUP:
		{
			g_rightMouseDown = false;
		}
		break;

		case WM_DESTROY:
		{
			PostQuitMessage(0);
		}
		break;

		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
				case ID_FILE_OPEN:

					// Get filename to open
					Filename[0] = 0;
					ZeroMemory(&ofn, sizeof(OPENFILENAME));
					ofn.lStructSize = sizeof(OPENFILENAME);
					ofn.nMaxFile = MAX_PATH;
					ofn.nMaxFileTitle = MAX_PATH;
					ofn.Flags = OFN_HIDEREADONLY | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
					ofn.hwndOwner   = hWnd;
					ofn.lpstrFile   = Filename;
					ofn.lpstrTitle  = "Load and Parse .x File";
					ofn.lpstrFilter = ".x Files (*.x)\0*.x\0All Files (*.*)\0*.*\0\0";
					ofn.lpstrDefExt = "x";
					if(!GetOpenFileName(&ofn))
					{
						return 0;
					}

					delete g_xFile;
					g_xFile = new stXFile;
					LoadXTxtFile(g_pD3DDevice, g_xFile, Filename);

				break;
			}
			break;

		case WM_DROPFILES:
		{
			HDROP hDrop = (HDROP)wParam;
			//Get Length of Filename Dragged Dropped
			int numFiles = 
				DragQueryFile(hDrop,	// HDROP
				-1,		// UINT
				NULL,		// LPSTR
				0);		// UINT

			if (numFiles<=0) break;

			//Get Length of Filename Dragged Dropped
			int strLen = 
				DragQueryFile(hDrop,
				0,		// FileIndex
				NULL,		
				0);

			DBG_ASSERT(strLen<255);

			char fileName[256] = {0};
			//Get Filename Dragged Dropped
			DragQueryFile(	hDrop,
							0,			// FileIndex
							fileName,	// FileName
							strLen+1);	// StringLen FileName

			bool valid = false;
			valid |= (strcmp(&fileName[strLen-2], ".x")==0);
			valid |= (strcmp(&fileName[strLen-2], ".X")==0);
			if (!valid) break;

			delete g_xFile;

			g_xFile = new stXFile;
			LoadXTxtFile(g_pD3DDevice, g_xFile, fileName);


		}
		break;

	}

	return (long)DefWindowProc(hWnd, uMsg, wParam, lParam);
}// End MsgProc(..)


//------------------------------------------------------------------------------

// msvcrt.lib

int _stdcall WinMain(HINSTANCE i, HINSTANCE j, char* k, int) 
{
	// Turn on memory leak checking
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)|_CRTDBG_LEAK_CHECK_DF);

	MSG msg;
	char szname[] = "DX X File Format TXT Loader - Parser/Drawer";
	WNDCLASSEX wc = {	sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
		GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
		szname, NULL };
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	RegisterClassEx( &wc );


	// Window menu 
	HMENU hmenu; 
	HMENU hmenupopup; 
	hmenu = CreateMenu(); 
	hmenupopup = CreateMenu(); 
	AppendMenu(hmenupopup, MF_STRING, ID_FILE_OPEN, "Open .x File"); 
	AppendMenu(hmenu, MF_POPUP,*((UINT*)&hmenupopup), "&File"); 


	g_hWnd = CreateWindowEx(	WS_EX_APPWINDOW,
		szname, szname, 
		WS_OVERLAPPEDWINDOW,
		50, 50, 1000,700,
		//60, 1000, 100,100,
		//50, 50, 840,700,
		GetDesktopWindow(), hmenu, wc.hInstance, NULL);


	DragAcceptFiles(g_hWnd, TRUE);

	CreateDX();
	IDirect3DDevice9* pd = g_pD3DDevice;

	g_xFile = new stXFile;

	//LoadXTxtFile(g_xFile, "media\\warrior.x");
	//LoadXTxtFile(g_xFile, "media\\warrior2.x");
	//LoadXTxtFile(g_xFile, "media\\can.x");
	//LoadXTxtFile(g_xFile, "media\\ninja.x");
	//LoadXTxtFile(g_xFile, "media\\anim.x");
	//LoadXTxtFile(g_xFile, "media\\frames.x");
	//LoadXTxtFile(g_xFile, "media\\ninja2.x");
	//LoadXTxtFile(g_xFile, "media\\customA.x");
	//LoadXTxtFile(g_xFile, "media\\smart.x");
	//LoadXTxtFile(g_xFile, "media\\DrXMesh.X");
	//LoadXTxtFile(g_xFile, "media\\cubeB.x");

	ShowWindow(g_hWnd, SW_SHOW);
	UpdateWindow(g_hWnd);

	while(1)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if (!GetMessage(&msg, NULL, 0, 0))
				break;
			DispatchMessage(&msg);
		}
		else
		{
			GameLoopDX();
		}
	}

	delete g_xFile;
	g_xFile = NULL;
	ReleaseDX();

	_CrtDumpMemoryLeaks();

}// End WinMain(..)


//------------------------------------------------------------------------------

















